class MyClass {
    // 构造函数
    constructor() {
        // 构造函数体
    }

    // 实例字段
    myField = "foo";

    // 实例方法
    myMethod() {

    }

    // 静态字段
    static myStaticField = "bar";

    // 静态方法
    static myStaticMethod() {

    }

    // 静态块
    static {

    }
    // 字段、方法、静态字段、静态方法、静态块都可以使用私有形式
    #myPrivateField = "bar";
}

// ---------------------------------------------------------------
// 声明可以作为字面量来使用
const MyClass = class YuYiClass {
    // 类体...
};
// new YuYiClass(); 不能直接访问
new MyClass(); // 当一个类，作为字面量，赋值给一个变量的时候，就只能通过被赋值的变量名来访问这个类


const myFunction = function yuYiFunction() {
    // 函数体
}
// yuYiFunction(); 不能直接访问
myFunction(); // 当一个函数，作为字面量，赋值给一个变量的时候，就只能通过被赋值的变量名来访问这个函数


/**
 * 构造函数与普通函数语法相同，也可以用 ...args 剩余参数，来接收数量不确定的参数
 */
class Color {
    constructor(r, g, b, ...config) {
        // 将 RGB 值作为 `this` 的属性
        this.values = [r, g, b];
        this.config = config;
    }

    getRed() {
        return this.values[0];
    }
}

const color = new Color();

// 复习和学新东西一样重要
// 学新东西：扩展知识面
// 复习：巩固已经学的东西

// 引用类型：在对象创建的过程中，每次创建都会在堆内开辟一块新的区域来存放对象
// 也就是说，即便是同一类，每次创建的对象，也不可能相同
const red = new Color(255, 0, 0);
const anotherRed = new Color(255, 0, 0);
console.log(red === anotherRed); // false
console.log(new Color().getRed === new Color().getRed); // false 不同对象的函数所指向的函数体，也不相同
const red2 = red; // 直接将引用赋值给新的变量，此时新的变量也会指向与 red 相同的对象。
console.log(red2 === red); // true

// 同一个类创建出的对象，相等吗？

class MyClass {
    constructor() {
        this.myField = "foo";
        // return {}; # 不要在 constructor 内return任何东西，会导致歧义，会让程序变得混乱
    }
}

class Color {
    constructor(r, g, b) {
        // 实例字段，也叫成员属性
        this.values = [r, g, b];
        this.getRed2(); // 通过属性名来访问

        // 可以在构造函数内部，通过下面两种方式来声明函数。但是，不推荐，会导致构造函数异常臃肿。
        // 1. 把函数当作字面量
        this.getRed4 = function () {
        }

        // 2. 把箭头函数当作字面量
        this.getRed5 = () => {
        }
    }

    // 以下是在类中，定义函数的三种方式。推荐这三种方式。

    // 1. 实例方法，也叫成员函数
    getRed() {
        return this.values[0];
    }

    // 2. 把函数当作字面量
    getRed2 = function () {
    }

    // 3. 把箭头函数当作字面量
    getRed3 = () => {
    }
}

const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255

/**
 * # private 私有化
 * 私有的目的，是保护内部的接口不被滥用
 */
class Car {
    #owner = {}; // 私有属性，以#开头。它与ts中的private具有相同的特征和作用。
    // #owner = {}; 相同名称不能重复两次
    constructor() {
        // VM3301:5 Uncaught SyntaxError: Private fields can not be deleted
        delete this.#owner; // 私有属性不能删除
    }
}

/**
 * 类的目的，是一个确定的 领域模型 的描述
 * 不要通过外部手段来影响 领域模型，比如 Object.defineProperty 这种行为是禁止的
 * 领域：一组相同事物的集合
 */
class Color { // 颜色类，颜色模型
    constructor(r, g, b) {
        // 凡是变量名前面没有 # 的，都是公共字段
        this.values = [r, g, b];
    }

    // get 访问拦截
    get red() {
        return this.values[0];
    }

    // set 赋值拦截
    set red(value) {
        this.values[0] = value;
    }

    static #privateVal = 1; // 静态私有字段

    static field = 1; // 静态属性
    static method() {
    } // 静态函数

    // 静态 getter setter
    static get field2() {
        return this.field;
    }

    static set field2(val) {
        this.field = val;
    }
}

const red = new Color(255, 0, 0);
red.red = 0;
console.log(red.red); // 0

Object.defineProperty({}, 'name', {
    set(val) {
    },
    get() {
    },
});

/**
 * 子类继承父类
 * 1. 子类会继承父类公共和被保护的属性或函数
 * 2. 子类[重载]父类函数，要满足以下要求：
 *  1. 函数名相同
 *  2. 参数 （个数，顺序，类型）相同
 *  3. 函数返回值的类型相同
 * 3. constructor 第一个行必须是 super()
 *
 * 非常非常非常重要：
 * 1. 面向对象原则，当你没有用好，或者过度滥用这个原则的时候，【它也会反噬你】
 * 2. 平衡，过度追求完美，你将会陷入深渊。
 * 3. 原则：用什么，写什么——如非必要，勿增实体。最小化原则，来设计类。
 */

/**
 * Map 映射类 键值对
 * Set 集合类 唯一性的值
 */

/**
 * 作业，将之前讲的所有东西，自己再吸收一边，看不懂就问，不会写就练
 * 31-2\3 多看，多练
 * 前面的内容，必须要熟练
 */